package com.gcloud.mesh.dcs.strategy.process;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.ecs20140526.models.*;
import com.aliyun.smc20190601.Client;
import com.aliyun.smc20190601.models.*;
import com.aliyun.teaopenapi.models.Config;
import com.gcloud.mesh.dcs.annotation.SchedulerLog;
import com.gcloud.mesh.header.vo.dcs.ALiVMInstantInfoVO;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.jeecg.common.exception.MyBusinessException;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 迁移过程方法
 */
@Component
@Slf4j
public class AliMigrationProcess {


    @SchedulerLog(stepName="查找迁移源")
    public String queryJobId(String jobId,Client client,String hostname){
        String id = Strings.EMPTY;
        DescribeSourceServersRequest describeSourceServersRequest = new DescribeSourceServersRequest()
                .setPageSize(50);
        // 复制代码运行请自行打印 API 的返回值
        try {
            List<DescribeSourceServersResponseBody.DescribeSourceServersResponseBodySourceServersSourceServer> sourceServer = client.describeSourceServers(describeSourceServersRequest).getBody().getSourceServers().getSourceServer();
            for (DescribeSourceServersResponseBody.DescribeSourceServersResponseBodySourceServersSourceServer describeSourceServersResponseBodySourceServersSourceServer : sourceServer) {
                String systemInfo = describeSourceServersResponseBodySourceServersSourceServer.getSystemInfo();
                JSONObject jsonObject = JSONObject.parseObject(systemInfo);
                String hn = jsonObject.getString("hostname");
                if(hn.equalsIgnoreCase(hostname)) {
                    id = describeSourceServersResponseBodySourceServersSourceServer.getSourceId();
                    break;
                }
            }
            if(StringUtils.isBlank(id)) throw new MyBusinessException("查找迁移源失败");
            log.debug("**********成功查找迁移源**********");
        } catch (Exception e) {
            log.error("查找迁移源失败",e);
            throw new MyBusinessException("查找迁移源失败");
        }
        return id;
    }


    @SchedulerLog(stepName="创建迁移镜像任务")
    public String createReplicationJob4Image (String jobId, Client client, String sourceId, Integer systemDiskSize, String regionId){

        BigDecimal bd = new BigDecimal(systemDiskSize.toString()).subtract(new BigDecimal("0.001"));
        BigDecimal bigDecimal = bd.multiply(new BigDecimal(1024).pow(3));
        CreateReplicationJobRequest.CreateReplicationJobRequestSystemDiskPart systemDiskPart0 = new CreateReplicationJobRequest.CreateReplicationJobRequestSystemDiskPart()
                .setSizeBytes(bigDecimal.longValue())
                .setDevice("0_0");

        //创建API请求，并设置参数
        CreateReplicationJobRequest request = new CreateReplicationJobRequest();
        //迁移源ID
        request.setSourceId(sourceId);
        //目标阿里云服务器ECS的系统盘大小，单位为GiB
        request.setSystemDiskSize(systemDiskSize);
        //保证请求幂等性，您可以从客户端生成一个不超过64个ASCII字符的参数值，并将值赋予ClientToken
//        request.setClientToken(UUID.randomUUID().toString());
//        request.setRunOnce(true);
        request.setRegionId(regionId);
        request.setSystemDiskPart(java.util.Arrays.asList(
                systemDiskPart0
        ));
        //发送请求获取返回值或处理异常
        CreateReplicationJobResponse response = null;
        try {
            response = client.createReplicationJob(request);
            log.debug(new Gson().toJson(response));
            log.debug("**********成功创建迁移镜像任务**********");
        } catch (Exception e) {
            log.error("创建迁移镜像任务失败",e);
            throw new MyBusinessException("创建迁移镜像任务失败");
        }
        return response.getBody().getJobId();


    }


    @SchedulerLog(stepName="创建迁移任务")
    public String createReplicationJob (String jobId, Client client, String sourceId, Integer systemDiskSize, String instanceId, String regionId){


        CreateReplicationJobRequest.CreateReplicationJobRequestSystemDiskPart systemDiskPart0 = new CreateReplicationJobRequest.CreateReplicationJobRequestSystemDiskPart()
                .setSizeBytes((systemDiskSize-1)*1024*1024*1024L)
                .setDevice("0_0");


        //创建API请求，并设置参数
        CreateReplicationJobRequest request = new CreateReplicationJobRequest();
        //迁移源ID
        request.setSourceId(sourceId);
        //目标阿里云服务器ECS的系统盘大小，单位为GiB
        request.setSystemDiskSize(systemDiskSize);
        //保证请求幂等性，您可以从客户端生成一个不超过64个ASCII字符的参数值，并将值赋予ClientToken
//        request.setClientToken(UUID.randomUUID().toString());
//        request.setRunOnce(true);
        request.setTargetType("TargetInstance");
        request.setInstanceId(instanceId);
        request.setRegionId(regionId);
        request.setSystemDiskPart(java.util.Arrays.asList(
                systemDiskPart0
        ));
        //发送请求获取返回值或处理异常
        CreateReplicationJobResponse response = null;
        try {
            response = client.createReplicationJob(request);
            log.debug(new Gson().toJson(response));
            log.debug("**********成功创建迁移任务**********");
        } catch (Exception e) {
            log.error("创建迁移任务失败",e);
            throw new MyBusinessException("创建迁移任务失败");
        }
        return response.getBody().getJobId();
    }

    @SchedulerLog(stepName="启动迁移任务")
    public void startReplicationJob(String jobId,Client client,String jobId_){
        //创建API请求，并设置参数
        StartReplicationJobRequest request = new StartReplicationJobRequest();
        //迁移任务ID
        request.setJobId(jobId_);
        //发送请求获取返回值或处理异常
        try {
            StartReplicationJobResponse response = client.startReplicationJob(request);
            log.debug(new Gson().toJson(response));
            log.debug("**********启动迁移任务**********");
        } catch (Exception e) {
            log.error("启动迁移任务失败",e);
            throw  new MyBusinessException("启动迁移任务失败");
        }
    }

    /**
     *
     * @param maxCheckTime 最大尝试次数
     */
    @SchedulerLog(stepName="查找目标迁移镜像")
    public Boolean findAndCheckImage(String jobId,String accessKeyId, String accessKeySecret,String destRegionId,String imageId,Integer maxCheckTime){
        com.aliyun.ecs20140526.Client client = createECSClient(accessKeyId,accessKeySecret,destRegionId);
        DescribeImagesRequest describeImagesRequest = new DescribeImagesRequest()
                .setImageId(imageId)
                .setRegionId(destRegionId);
        // 复制代码运行请自行打印 API 的返回值
        try {
            TimeUnit.SECONDS.sleep(5);
            Integer totalCount = client.describeImages(describeImagesRequest).getBody().totalCount;
            return totalCount>0?true:false;
        } catch (Exception e) {
            if(maxCheckTime!=null && maxCheckTime-->0){
                return findAndCheckImage(jobId,accessKeyId,accessKeySecret,destRegionId,imageId,maxCheckTime);
            }
            log.error("发现并验证迁移镜像失败",e);
            throw new MyBusinessException("发现并验证迁移镜像失败");
        }
    }


    /**
     * 获取实例信息
     * @param accessKeyId
     * @param accessKeySecret
     * @param srcRegionId
     * @param srcInstanceId
     * @return
     */
    @SchedulerLog(stepName="获取源虚拟机实例信息")
    public ALiVMInstantInfoVO getSrcInstanceInfo(String jobId,String accessKeyId, String accessKeySecret,String srcRegionId,String srcInstanceId,Integer maxCheckTime){
        ALiVMInstantInfoVO vo = new ALiVMInstantInfoVO();
        try {
            TimeUnit.SECONDS.sleep(5);
            com.aliyun.ecs20140526.Client ecsClient = createECSClient(accessKeyId, accessKeySecret, srcRegionId);
            DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest()
                    .setRegionId(srcRegionId)
                    .setInstanceIds("[\""+srcInstanceId+"\"]");
            // 复制代码运行请自行打印 API 的返回值
            DescribeInstancesResponseBody.DescribeInstancesResponseBodyInstancesInstance ii = ecsClient.describeInstances(describeInstancesRequest).getBody().getInstances().getInstance().get(0);
            vo.setInstanceType(ii.getInstanceType());
            vo.setInstanceChargeType(ii.getInstanceChargeType());
            vo.setInternetChargeType(ii.getInternetChargeType());
            vo.setInternetMaxBandwidthIn(ii.getInternetMaxBandwidthIn());
            vo.setInternetMaxBandwidthOut(ii.getInternetMaxBandwidthOut());
            vo.setHostName(ii.getHostName());
            vo.setInstanceId(ii.getInstanceId());
        } catch (Exception e) {
            if(maxCheckTime!=null && maxCheckTime-->0){
                return getSrcInstanceInfo(jobId,accessKeyId,accessKeySecret,srcRegionId,srcInstanceId,maxCheckTime);
            }
            log.error("获取源虚拟机实例信息失败",e);
            throw new MyBusinessException("获取源虚拟机实例信息失败");
        }
        return vo;
    }


    @SchedulerLog(stepName="创建实例")
    public String createInstance(String jobId,String accessKeyId, String accessKeySecret , String regionId, String imageId, ALiVMInstantInfoVO info){
        try {
            com.aliyun.ecs20140526.Client client =  createECSClient(accessKeyId,accessKeySecret,regionId);
            CreateInstanceRequest createInstanceRequest = new CreateInstanceRequest()
                    .setRegionId(regionId)
                    .setImageId(imageId)
                    .setInstanceType(info.getInstanceType())
                    .setInternetChargeType(info.getInternetChargeType())
                    .setInternetMaxBandwidthIn(info.getInternetMaxBandwidthIn())
                    .setInternetMaxBandwidthOut(info.getInternetMaxBandwidthOut())
                    .setPasswordInherit(false)
                    .setHostName(info.getHostName())
                    .setInstanceChargeType(info.getInstanceChargeType())
                    .setPassword("gcloud123!@#");
            // 复制代码运行请自行打印 API 的返回值
            return client.createInstance(createInstanceRequest).getBody().getInstanceId();
        } catch (Exception e) {
            log.error("创建实例失败",e);
            throw new MyBusinessException("创建实例失败");
        }
    }

    @SchedulerLog(stepName="启动实例")
    public String startInstance(String jobId,String accessKeyId, String accessKeySecret , String regionId,String InstanceId,Integer maxCheckTime){
        try {
            TimeUnit.SECONDS.sleep(5);
            com.aliyun.ecs20140526.Client client =  createECSClient(accessKeyId,accessKeySecret,regionId);
            StartInstanceRequest startInstanceRequest = new StartInstanceRequest()
                    .setInstanceId(InstanceId);
            // 复制代码运行请自行打印 API 的返回值
            return client.startInstance(startInstanceRequest).getBody().getRequestId();
        } catch (Exception e) {
            if(maxCheckTime!=null && maxCheckTime-->0){
                return startInstance(jobId,accessKeyId,accessKeySecret,regionId,InstanceId,maxCheckTime);
            }
            log.error("启动实例失败",e);
            throw new MyBusinessException("启动实例失败");
        }

    }


    @SchedulerLog(stepName="分配实例公网IP")
    public String allocatePublicIpAddress(String jobId,String accessKeyId, String accessKeySecret , String regionId,String InstanceId,Integer maxCheckTime){
        try {
            TimeUnit.SECONDS.sleep(5);
            com.aliyun.ecs20140526.Client client = createECSClient(accessKeyId,accessKeySecret,regionId);
            AllocatePublicIpAddressRequest allocatePublicIpAddressRequest = new AllocatePublicIpAddressRequest()
                    .setInstanceId(InstanceId);
            return client.allocatePublicIpAddress(allocatePublicIpAddressRequest).getBody().getIpAddress();
        } catch (Exception e) {
            if(maxCheckTime!=null && maxCheckTime-->0){
                return allocatePublicIpAddress(jobId,accessKeyId,accessKeySecret,regionId,InstanceId,maxCheckTime);
            }
            log.error("分配实例公网IP失败",e);
            throw new MyBusinessException("分配实例公网IP失败");
        }
    }

    public  com.aliyun.ecs20140526.Client createECSClient(String accessKeyId, String accessKeySecret,String regionId) {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                // 您的AccessKey ID
                .setAccessKeyId(accessKeyId)
                // 您的AccessKey Secret
                .setAccessKeySecret(accessKeySecret);
        // 访问的域名
        config.endpoint = "ecs."+regionId+".aliyuncs.com";
        try {
             return new com.aliyun.ecs20140526.Client(config);
        } catch (Exception e) {
            log.error("创建阿里云同步连接失败",e);
            throw new MyBusinessException("创建阿里云同步连接失败");
        }
    }


    public  Client createSMCClient(String accessKeyId, String accessKeySecret) {
        Config config = new Config()
                // 您的AccessKey ID
                .setAccessKeyId(accessKeyId)
                // 您的AccessKey Secret
                .setAccessKeySecret(accessKeySecret);
        // 访问的域名
        config.endpoint = "smc.aliyuncs.com";
        try {
            return  new Client(config);
        } catch (Exception e) {
            log.error("创建阿里云同步连接失败",e);
            throw new MyBusinessException("创建阿里云同步连接失败");
        }
    }


    public DescribeReplicationJobsResponseBody.DescribeReplicationJobsResponseBodyReplicationJobsReplicationJob describeReplicationJobs(String accessKeyId, String accessKeySecret,String jobId_,String sourceId){

        Client smcClient = this.createSMCClient(accessKeyId, accessKeySecret);

        //创建API请求，并设置参数
        DescribeReplicationJobsRequest request = new DescribeReplicationJobsRequest();
        //设置迁移任务的页码
        request.setPageNumber(1);
        //设置每页行数
        request.setPageSize(10);
        //设置迁移任务的名称
//        request.setName("MigrationTask");
        //设置迁移任务ID列表
        List<String> jobIds = new ArrayList<>();
        jobIds.add(jobId_);
        request.setJobId(jobIds);
        //设置迁移源ID列表
        List<String> sourceIds = new ArrayList<>();
        sourceIds.add(sourceId);
        request.setSourceId(sourceIds);

        //发送请求获取返回值或处理异常
        DescribeReplicationJobsResponse response = null;
        try {
            response = smcClient.describeReplicationJobs(request);
            log.debug(new Gson().toJson(response));
            log.debug("**********查询迁移任务状态**********");
        } catch (Exception e) {
            log.error("查询迁移任务状态失败",e);
            throw  new MyBusinessException("查询迁移任务状态失败");
        }
        List<DescribeReplicationJobsResponseBody.DescribeReplicationJobsResponseBodyReplicationJobsReplicationJob> replicationJobList = response.getBody().getReplicationJobs().getReplicationJob();
        return (replicationJobList==null || replicationJobList.isEmpty())? null:replicationJobList.get(0);
    }

    @SchedulerLog(stepName="删除源虚拟机实例")
    public void deleteSrcInstanct(String jobId,String accessKeyId, String accessKeySecret , String regionId,String InstanceId){
        com.aliyun.ecs20140526.Client ecsClient = this.createECSClient(accessKeyId, accessKeySecret,regionId);
        DeleteInstancesRequest deleteInstancesRequest = new DeleteInstancesRequest()
                .setRegionId(regionId)
                .setForce(true)
                .setInstanceId(java.util.Arrays.asList(
                        InstanceId
                ));
        try {
            ecsClient.deleteInstances(deleteInstancesRequest);
        } catch (Exception e) {
            log.error("删除源虚拟机失败",e);
            throw  new MyBusinessException("删除源虚拟机");
        }
    }


}
